import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.BeanUtilsBean2;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @author zhangdi
 * @version 1.0
 * @date 2021.11.12 14:15
 */
public class ReflectTest<T> {
    public <T> void get(Class<T> clazz) {
        getE(clazz);
    }

    public void getE(Class<?> clazz) {
        System.out.println(clazz.getSimpleName());
    }

    public static void main(String[] args) {
        ObjectTestD testD = new ObjectTestD();
        System.out.println(testD.getId());
        ObjectTestAAA aaa = new ObjectTestAAA();
        aaa.setArea("哈哈会");
        testD.add(aaa);
        System.out.println(testD.getId());
        for (ObjectTestAAA area : testD.getAreas()) {
            System.out.println(area.getArea());
        }

//        Map<String, Object> map = new HashMap<>();
//        map.put("id", 111);
//        map.put("name", "明明蘑菇蘑菇蘑菇");
//
//        List<Map<String, Object>> areas = new ArrayList<>();
//        for (int i = 0; i < 5; i++) {
//            Map<String, Object> area = new HashMap<>();
//            area.put("id", 3223);
//            area.put("area", "阿斯蒂芬扣篮得分手段");
//            areas.add(area);
//        }
//        map.put("areas", areas);

//        List<ObjectTestD> list = new ArrayList<>();
//        for (int i = 0; i < 3; i++) {
//            ObjectTestD child = new ObjectTestD();
//            child.setArea("d_area"+i);
//            child.setId(i);
//            child.setName("name"+i);
//            child.setAreas(new ArrayList<>());
//            for (int j = 0; j < 2; j++) {
//                ObjectTestAAA area= new ObjectTestAAA();
//                area.setArea("aaa"+j);
//                child.getAreas().add(area);
//            }
//
//            list.add(child);
//        }
//
//
//        ReflectTest<ObjectTestD> test = new ReflectTest<>();
//        List<Map<String, Object>> maps = test.ListBeanToListMap(list);
//        System.out.println("1");

//        Map<String, Object> aaa = new HashMap<>();
//        aaa.put("op", null);
//        System.out.println((String) aaa.get("op"));
    }

    public T mapToBean(Class<T> type, Map<String, Object> map) {
        T obj = null;
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(type);
            obj = type.newInstance();
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor descriptor : propertyDescriptors) {
                String propertyName = descriptor.getName();
                if (map.containsKey(propertyName)) {
                    Object value = map.get(propertyName);
                    if (value instanceof Collection<?>) {
                        Collection<Object> objectCollection = new ArrayList<>();
                        Iterator<?> iterator = ((Collection<?>) value).iterator();
                        while (iterator.hasNext()) {
                            Object item = iterator.next();
                            if (item instanceof HashMap) {
                                Field field = findField(type, propertyName);
                                if (field != null) {
                                    Type genericType = field.getGenericType();
                                    if(genericType instanceof ParameterizedType){
                                        ParameterizedType pt = (ParameterizedType) genericType;
                                        Class<?> actualTypeArgument = (Class<?>) pt.getActualTypeArguments()[0];
                                        Object genericObj = actualTypeArgument.newInstance();
                                        Field[] genericObjFields = actualTypeArgument.getDeclaredFields();
                                        Map<Object, Object> elMap = (HashMap<Object, Object>) item;
                                        for (Field genericObjField : genericObjFields) {
                                            genericObjField.setAccessible(true);
                                            if(elMap.containsKey(genericObjField.getName())){
                                                genericObjField.set(genericObj, elMap.get(genericObjField.getName()));
                                            }
                                        }
                                        objectCollection.add(genericObj);
                                    }
                                } else {
                                    objectCollection.add(item);
                                }
                            } else {
                                objectCollection.add(item);
                            }
                        }
                        descriptor.getWriteMethod().invoke(obj, objectCollection);
                    } else {
                        descriptor.getWriteMethod().invoke(obj, value);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }

    public <K, Tt> List<Map<K, Object>> ListBeanToListMap(List<Tt> list) {
        List<Map<K, Object>> mapList = new ArrayList<>();
        if (null == list || list.size() == 0) {
            return mapList;
        }
        for (Tt tt : list) {
            List<Field> fields = new ArrayList<>();
            getAllField(tt.getClass(), fields);
            if (fields.size() > 0) {
                Map<K, Object> map = new HashMap<>();
                for (Field field : fields) {
                    field.setAccessible(true);
                    try {
                        Object value = field.get(tt);
                        if (value != null) {
                            map.put((K) field.getName(), value);
                        }
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }

                if (map.size() > 0) {
                    mapList.add(map);
                }
            }
        }

        return mapList;
    }

    private Field findField(Class<?> clazz, String propertyName) {
        if (clazz == null) {
            return null;
        }
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (field.getName().equals(propertyName)) {
                return field;
            }
        }

        return this.findField(clazz.getSuperclass(), propertyName);
    }

    private void getAllField(Class<?> clazz, List<Field> allFields) {
        if (clazz == null) {
            return;
        }
        Field[] fields = clazz.getDeclaredFields();
        if(fields.length > 0){
            allFields.addAll(Arrays.asList(fields));
        }

        Class<?> superCLazz = clazz.getSuperclass();
        if(superCLazz!=null){
            this.getAllField(superCLazz, allFields);
        }
    }
}
